உங்கள் பைத்தான் குறியீட்டின் செயல்திறனை பல மடங்கு அதிகரிக்கவும். இந்த விரிவான வழிகாட்டி உலகளாவிய டெவலப்பர்களுக்காக SIMD, வெக்டரைசேஷன், NumPy மற்றும் மேம்பட்ட நூலகங்களை ஆராய்கிறது.
செயல்திறனைத் திறத்தல்: பைத்தான் SIMD மற்றும் வெக்டரைசேஷனுக்கான ஒரு விரிவான வழிகாட்டி
கணினி உலகில், வேகம் மிக முக்கியமானது. நீங்கள் ஒரு இயந்திர கற்றல் மாதிரியைப் பயிற்றுவிக்கும் ஒரு தரவு விஞ்ஞானியாக இருந்தாலும், ஒரு உருவகப்படுத்துதலை இயக்கும் நிதி ஆய்வாளராக இருந்தாலும், அல்லது பெரிய தரவுத்தொகுப்புகளை செயலாக்கும் ஒரு மென்பொருள் பொறியாளராக இருந்தாலும், உங்கள் குறியீட்டின் செயல்திறன் உற்பத்தித்திறன் மற்றும் வள நுகர்வை நேரடியாக பாதிக்கிறது. அதன் எளிமை மற்றும் வாசிப்புத்திறனுக்காக கொண்டாடப்படும் பைத்தான், கணக்கீட்டு ரீதியாக தீவிரமான பணிகளில், குறிப்பாக சுழற்சிகளை உள்ளடக்கிய பணிகளில் அதன் செயல்திறனில் ஒரு அறியப்பட்ட பலவீனத்தைக் கொண்டுள்ளது. ஆனால் ஒரு நேரத்தில் ஒரு உறுப்புக்கு பதிலாக, ஒரே நேரத்தில் முழு தரவுத் தொகுப்புகளிலும் செயல்பாடுகளைச் செய்ய முடிந்தால் என்ன செய்வது? இது வெக்டரைஸ்டு கணக்கீட்டின் வாக்குறுதியாகும், இது SIMD எனப்படும் CPU அம்சத்தால் இயக்கப்படும் ஒரு முன்னுதாரணமாகும்.
இந்த வழிகாட்டி உங்களை பைத்தானில் உள்ள ஒற்றை அறிவுறுத்தல், பல தரவு (SIMD) செயல்பாடுகள் மற்றும் வெக்டரைசேஷன் உலகிற்கு ஒரு ஆழமான பயணத்திற்கு அழைத்துச் செல்லும். CPU கட்டமைப்பின் அடிப்படைக் கருத்துக்களிலிருந்து NumPy, Numba, மற்றும் Cython போன்ற சக்திவாய்ந்த நூலகங்களின் நடைமுறைப் பயன்பாடு வரை நாம் பயணிப்போம். உங்கள் புவியியல் இருப்பிடம் அல்லது பின்னணியைப் பொருட்படுத்தாமல், உங்கள் மெதுவான, சுழற்சியான பைத்தான் குறியீட்டை மிகவும் உகந்ததாக்கப்பட்ட, உயர் செயல்திறன் கொண்ட பயன்பாடுகளாக மாற்றுவதற்கான அறிவை உங்களுக்கு வழங்குவதே எங்கள் குறிக்கோள்.
அடித்தளம்: CPU கட்டமைப்பு மற்றும் SIMD-ஐப் புரிந்துகொள்ளுதல்
வெக்டரைசேஷனின் சக்தியை உண்மையாகப் பாராட்ட, ஒரு நவீன மத்திய செயலாக்க அலகு (CPU) எவ்வாறு செயல்படுகிறது என்பதை நாம் முதலில் பார்க்க வேண்டும். SIMD-இன் மந்திரம் ஒரு மென்பொருள் தந்திரம் அல்ல; இது எண் கணினியில் புரட்சியை ஏற்படுத்திய ஒரு வன்பொருள் திறனாகும்.
SISD-இலிருந்து SIMD-க்கு: கணக்கீட்டில் ஒரு முன்னுதாரண மாற்றம்
பல ஆண்டுகளாக, கணக்கீட்டின் மேலாதிக்க மாதிரியாக SISD (ஒற்றை அறிவுறுத்தல், ஒற்றை தரவு) இருந்தது. ஒரு சமையல்காரர் ஒரு நேரத்தில் ஒரு காய்கறியை உன்னிப்பாக நறுக்குவதை கற்பனை செய்து பாருங்கள். சமையல்காரருக்கு ஒரு அறிவுறுத்தல் ("நறுக்கு") உள்ளது மற்றும் ஒரு தரவுத் துண்டில் (ஒரு கேரட்) செயல்படுகிறது. இது ஒரு பாரம்பரிய CPU கோர் ஒரு சுழற்சிக்கு ஒரு தரவுத் துண்டில் ஒரு அறிவுறுத்தலைச் செயல்படுத்துவதற்கு ஒப்பானது. இரண்டு பட்டியல்களில் இருந்து எண்களை ஒவ்வொன்றாகச் சேர்க்கும் ஒரு எளிய பைத்தான் லூப் SISD மாதிரியின் சரியான எடுத்துக்காட்டு:
# கருத்தியல் SISD செயல்பாடு
result = []
for i in range(len(list_a)):
# ஒரு நேரத்தில் ஒரு தரவுத் துண்டில் (a[i], b[i]) ஒரு அறிவுறுத்தல் (கூட்டல்)
result.append(list_a[i] + list_b[i])
இந்த அணுகுமுறை வரிசையானது மற்றும் ஒவ்வொரு மறு செய்கைக்கும் பைத்தான் மொழிபெயர்ப்பாளரிடமிருந்து குறிப்பிடத்தக்க மேல்நிலையை ஏற்படுத்துகிறது. இப்போது, அந்த சமையல்காரருக்கு ஒரு நெம்புகோலை ஒரே இழுவையில் ஒரே நேரத்தில் நான்கு கேரட் வரிசையை நறுக்கக்கூடிய ஒரு சிறப்பு இயந்திரத்தைக் கொடுப்பதை கற்பனை செய்து பாருங்கள். இதுவே SIMD (ஒற்றை அறிவுறுத்தல், பல தரவு)-இன் சாராம்சம். CPU ஒரு ஒற்றை அறிவுறுத்தலை வெளியிடுகிறது, ஆனால் அது ஒரு சிறப்பு, அகலமான பதிவேட்டில் ஒன்றாக நிரம்பிய பல தரவுப் புள்ளிகளில் செயல்படுகிறது.
நவீன CPU-க்களில் SIMD எவ்வாறு செயல்படுகிறது
இன்டெல் மற்றும் ஏஎம்டி போன்ற உற்பத்தியாளர்களிடமிருந்து வரும் நவீன CPU-க்கள் இந்த இணை செயல்பாடுகளைச் செய்ய சிறப்பு SIMD பதிவேடுகள் மற்றும் அறிவுறுத்தல் தொகுப்புகளுடன் பொருத்தப்பட்டுள்ளன. இந்தப் பதிவேடுகள் பொது-நோக்கப் பதிவேடுகளை விட மிகவும் அகலமானவை மற்றும் ஒரே நேரத்தில் பல தரவு உறுப்புகளை வைத்திருக்க முடியும்.
- SIMD பதிவேடுகள்: இவை CPU-வில் உள்ள பெரிய வன்பொருள் பதிவேடுகள். அவற்றின் அளவுகள் காலப்போக்கில் உருவாகியுள்ளன: 128-பிட், 256-பிட், மற்றும் இப்போது 512-பிட் பதிவேடுகள் பொதுவானவை. ஒரு 256-பிட் பதிவேடு, எடுத்துக்காட்டாக, எட்டு 32-பிட் மிதக்கும்-புள்ளி எண்களை அல்லது நான்கு 64-பிட் மிதக்கும்-புள்ளி எண்களை வைத்திருக்க முடியும்.
- SIMD அறிவுறுத்தல் தொகுப்புகள்: CPU-க்களுக்கு இந்தப் பதிவேடுகளுடன் வேலை செய்ய குறிப்பிட்ட அறிவுறுத்தல்கள் உள்ளன. இந்த சுருக்கங்களை நீங்கள் கேள்விப்பட்டிருக்கலாம்:
- SSE (Streaming SIMD Extensions): ஒரு பழைய 128-பிட் அறிவுறுத்தல் தொகுப்பு.
- AVX (Advanced Vector Extensions): ஒரு 256-பிட் அறிவுறுத்தல் தொகுப்பு, குறிப்பிடத்தக்க செயல்திறன் ஊக்கத்தை வழங்குகிறது.
- AVX2: அதிக அறிவுறுத்தல்களுடன் AVX-இன் ஒரு நீட்டிப்பு.
- AVX-512: பல நவீன சர்வர் மற்றும் உயர்-நிலை டெஸ்க்டாப் CPU-க்களில் காணப்படும் ஒரு சக்திவாய்ந்த 512-பிட் அறிவுறுத்தல் தொகுப்பு.
இதை நாம் காட்சிப்படுத்துவோம். நாம் இரண்டு வரிசைகளை, `A = [1, 2, 3, 4]` மற்றும் `B = [5, 6, 7, 8]` ஆகியவற்றைச் சேர்க்க விரும்புகிறோம் என்று வைத்துக்கொள்வோம், இங்கு ஒவ்வொரு எண்ணும் 32-பிட் முழு எண் ஆகும். 128-பிட் SIMD பதிவேடுகளைக் கொண்ட ஒரு CPU-வில்:
- CPU `[1, 2, 3, 4]`-ஐ SIMD பதிவேடு 1-இல் ஏற்றுகிறது.
- CPU `[5, 6, 7, 8]`-ஐ SIMD பதிவேடு 2-இல் ஏற்றுகிறது.
- CPU ஒரு ஒற்றை வெக்டரைஸ்டு "கூட்டல்" அறிவுறுத்தலை (`_mm_add_epi32` என்பது ஒரு உண்மையான அறிவுறுத்தலின் எடுத்துக்காட்டு) செயல்படுத்துகிறது.
- ஒரே கடிகார சுழற்சியில், வன்பொருள் நான்கு தனித்தனி கூட்டல்களை இணையாகச் செய்கிறது: `1+5`, `2+6`, `3+7`, `4+8`.
- விளைவு, `[6, 8, 10, 12]`, மற்றொரு SIMD பதிவேட்டில் சேமிக்கப்படுகிறது.
இது SISD அணுகுமுறையை விட முக்கிய கணக்கீட்டிற்கு 4 மடங்கு வேகமானது, அறிவுறுத்தல் அனுப்புதல் மற்றும் லூப் மேல்நிலை ஆகியவற்றில் ஏற்பட்ட பெரும் குறைப்பைக் கணக்கிடாமலேயே.
செயல்திறன் இடைவெளி: ஸ்கேலார் மற்றும் வெக்டர் செயல்பாடுகள்
ஒரு பாரம்பரிய, ஒரு நேரத்தில் ஒரு உறுப்பு செயல்பாட்டிற்கான சொல் ஒரு ஸ்கேலார் செயல்பாடு ஆகும். ஒரு முழு வரிசை அல்லது தரவு வெக்டரில் ஒரு செயல்பாடு ஒரு வெக்டர் செயல்பாடு ஆகும். செயல்திறன் வேறுபாடு நுட்பமானதல்ல; அது பல மடங்கு இருக்கலாம்.
- குறைக்கப்பட்ட மேல்நிலை: பைத்தானில், ஒரு லூப்பின் ஒவ்வொரு மறு செய்கைக்கும் மேல்நிலை உள்ளது: லூப் நிபந்தனையைச் சரிபார்த்தல், கவுண்டரை அதிகரித்தல், மற்றும் மொழிபெயர்ப்பாளர் மூலம் செயல்பாட்டை அனுப்புதல். ஒரு ஒற்றை வெக்டர் செயல்பாட்டிற்கு ஒரே ஒரு அனுப்புதல் மட்டுமே உள்ளது, வரிசை ஆயிரம் அல்லது ஒரு மில்லியன் உறுப்புகளைக் கொண்டிருந்தாலும் சரி.
- வன்பொருள் இணைத்தன்மை: நாம் பார்த்தது போல், SIMD நேரடியாக ஒரு CPU கோருக்குள் உள்ள இணை செயலாக்க அலகுகளைப் பயன்படுத்துகிறது.
- மேம்படுத்தப்பட்ட கேச் இருப்பிடம்: வெக்டரைஸ்டு செயல்பாடுகள் பொதுவாக நினைவகத்தின் தொடர்ச்சியான தொகுதிகளிலிருந்து தரவைப் படிக்கின்றன. இது CPU-இன் கேச்சிங் அமைப்பிற்கு மிகவும் திறமையானது, இது வரிசையான துண்டுகளில் தரவை முன்-பெறுவதற்கு வடிவமைக்கப்பட்டுள்ளது. லூப்களில் சீரற்ற அணுகல் முறைகள் அடிக்கடி "கேச் மிஸ்"-களுக்கு வழிவகுக்கும், அவை நம்பமுடியாத அளவிற்கு மெதுவானவை.
பைத்தானிக் வழி: NumPy உடன் வெக்டரைசேஷன்
வன்பொருளைப் புரிந்துகொள்வது கவர்ச்சிகரமானது, ஆனால் அதன் சக்தியைப் பயன்படுத்த நீங்கள் குறைந்த-நிலை அசெம்பிளி குறியீட்டை எழுதத் தேவையில்லை. பைத்தான் சுற்றுச்சூழல் அமைப்பில் வெக்டரைசேஷனை அணுகக்கூடியதாகவும் உள்ளுணர்வுடனும் செய்யும் ஒரு அற்புதமான நூலகம் உள்ளது: NumPy.
NumPy: பைத்தானில் அறிவியல் கணினிக்கு அடித்தளம்
NumPy பைத்தானில் எண் கணக்கீட்டிற்கான அடிப்படை தொகுப்பு ஆகும். அதன் முக்கிய அம்சம் சக்திவாய்ந்த N-பரிமாண வரிசை பொருள், `ndarray` ஆகும். NumPy-இன் உண்மையான மந்திரம் என்னவென்றால், அதன் மிக முக்கியமான நடைமுறைகள் (கணித செயல்பாடுகள், வரிசை கையாளுதல் போன்றவை) பைத்தானில் எழுதப்படவில்லை. அவை மிகவும் உகந்ததாக்கப்பட்ட, முன்-தொகுக்கப்பட்ட C அல்லது ஃபோர்டிரான் குறியீடாகும், இது BLAS (Basic Linear Algebra Subprograms) மற்றும் LAPACK (Linear Algebra Package) போன்ற குறைந்த-நிலை நூலகங்களுடன் இணைக்கப்பட்டுள்ளது. இந்த நூலகங்கள் பெரும்பாலும் ஹோஸ்ட் CPU-வில் கிடைக்கும் SIMD அறிவுறுத்தல் தொகுப்புகளை உகந்த முறையில் பயன்படுத்த விற்பனையாளரால் சரிசெய்யப்படுகின்றன.
நீங்கள் NumPy-இல் `C = A + B` என்று எழுதும்போது, நீங்கள் ஒரு பைத்தான் லூப்பை இயக்கவில்லை. நீங்கள் SIMD அறிவுறுத்தல்களைப் பயன்படுத்தி கூட்டலைச் செய்யும் மிகவும் உகந்ததாக்கப்பட்ட C செயல்பாட்டிற்கு ஒரு ஒற்றை கட்டளையை அனுப்புகிறீர்கள்.
நடைமுறை எடுத்துக்காட்டு: பைத்தான் லூப்பிலிருந்து NumPy வரிசைக்கு
இதை செயலில் பார்ப்போம். நாங்கள் இரண்டு பெரிய எண்களின் வரிசைகளைச் சேர்ப்போம், முதலில் தூய பைத்தான் லூப் உடனும் பின்னர் NumPy உடனும். உங்கள் சொந்த கணினியில் முடிவுகளைக் காண இந்த குறியீட்டை நீங்கள் ஒரு ஜூபிடர் நோட்புக் அல்லது ஒரு பைத்தான் ஸ்கிரிப்டில் இயக்கலாம்.
முதலில், தரவை அமைப்போம்:
import time
import numpy as np
# ஒரு பெரிய எண்ணிக்கையிலான உறுப்புகளைப் பயன்படுத்துவோம்
num_elements = 10_000_000
# தூய பைத்தான் பட்டியல்கள்
list_a = [i * 0.5 for i in range(num_elements)]
list_b = [i * 0.2 for i in range(num_elements)]
# NumPy வரிசைகள்
array_a = np.arange(num_elements) * 0.5
array_b = np.arange(num_elements) * 0.2
இப்போது, தூய பைத்தான் லூப்பிற்கான நேரத்தைக் கணக்கிடுவோம்:
start_time = time.time()
result_list = [0] * num_elements
for i in range(num_elements):
result_list[i] = list_a[i] + list_b[i]
end_time = time.time()
python_duration = end_time - start_time
print(f"தூய பைத்தான் லூப் எடுத்தது: {python_duration:.6f} வினாடிகள்")
இப்போது, அதற்கு சமமான NumPy செயல்பாடு:
start_time = time.time()
result_array = array_a + array_b
end_time = time.time()
numpy_duration = end_time - start_time
print(f"NumPy வெக்டரைஸ்டு செயல்பாடு எடுத்தது: {numpy_duration:.6f} வினாடிகள்")
# வேக அதிகரிப்பைக் கணக்கிடுங்கள்
if numpy_duration > 0:
print(f"NumPy தோராயமாக {python_duration / numpy_duration:.2f}x மடங்கு வேகமானது.")
ஒரு பொதுவான நவீன கணினியில், வெளியீடு மலைக்க வைக்கும். NumPy பதிப்பு 50 முதல் 200 மடங்கு வேகமாக இருக்கும் என்று நீங்கள் எதிர்பார்க்கலாம். இது ஒரு சிறிய தேர்வுமுறை அல்ல; இது கணக்கீடு எவ்வாறு செய்யப்படுகிறது என்பதில் ஒரு அடிப்படை மாற்றமாகும்.
உலகளாவிய செயல்பாடுகள் (ufuncs): NumPy-ன் வேகத்தின் இயந்திரம்
நாம் இப்போது செய்த செயல்பாடு (`+`) ஒரு NumPy உலகளாவிய செயல்பாடு, அல்லது ufunc-இன் ஒரு எடுத்துக்காட்டு ஆகும். இவை `ndarray`-களில் ஒரு உறுப்பு-மூலம்-உறுப்பு முறையில் செயல்படும் செயல்பாடுகள். அவை NumPy-இன் வெக்டரைஸ்டு சக்தியின் மையமாகும்.
ufuncs-இன் எடுத்துக்காட்டுகள் பின்வருமாறு:
- கணித செயல்பாடுகள்: `np.add`, `np.subtract`, `np.multiply`, `np.divide`, `np.power`.
- திரிகோணமிதி செயல்பாடுகள்: `np.sin`, `np.cos`, `np.tan`.
- தருக்க செயல்பாடுகள்: `np.logical_and`, `np.logical_or`, `np.greater`.
- அடுக்குக்குறி மற்றும் மடக்கை செயல்பாடுகள்: `np.exp`, `np.log`.
நீங்கள் ஒரு வெளிப்படையான லூப்பை எழுதாமலேயே சிக்கலான சூத்திரங்களை வெளிப்படுத்த இந்த செயல்பாடுகளை ஒன்றாக இணைக்கலாம். ஒரு காஸியன் செயல்பாட்டைக் கணக்கிடுவதைக் கவனியுங்கள்:
# x என்பது ஒரு மில்லியன் புள்ளிகளைக் கொண்ட NumPy வரிசை
x = np.linspace(-5, 5, 1_000_000)
# ஸ்கேலார் அணுகுமுறை (மிகவும் மெதுவானது)
result = []
for val in x:
term = -0.5 * (val ** 2)
result.append((1 / np.sqrt(2 * np.pi)) * np.exp(term))
# வெக்டரைஸ்டு NumPy அணுகுமுறை (மிகவும் வேகமானது)
result_vectorized = (1 / np.sqrt(2 * np.pi)) * np.exp(-0.5 * x**2)
வெக்டரைஸ்டு பதிப்பு வியத்தகு முறையில் வேகமானது மட்டுமல்லாமல், எண் கணினி தெரிந்தவர்களுக்கு மிகவும் சுருக்கமாகவும் படிக்கக்கூடியதாகவும் உள்ளது.
அடிப்படைகளுக்கு அப்பால்: பிராட்காஸ்டிங் மற்றும் நினைவக தளவமைப்பு
NumPy-இன் வெக்டரைசேஷன் திறன்கள் பிராட்காஸ்டிங் எனப்படும் ஒரு கருத்தால் மேலும் மேம்படுத்தப்பட்டுள்ளன. இது கணித செயல்பாடுகளின் போது NumPy வெவ்வேறு வடிவங்களைக் கொண்ட வரிசைகளை எவ்வாறு கையாளுகிறது என்பதை விவரிக்கிறது. பிராட்காஸ்டிங் ஒரு பெரிய வரிசைக்கும் ஒரு சிறிய வரிசைக்கும் (எ.கா., ஒரு ஸ்கேலார்) இடையில் செயல்பாடுகளைச் செய்ய உங்களை அனுமதிக்கிறது, பெரிய வரிசையின் வடிவத்துடன் பொருந்த சிறிய வரிசையின் நகல்களை வெளிப்படையாக உருவாக்காமல். இது நினைவகத்தைச் சேமிக்கிறது மற்றும் செயல்திறனை மேம்படுத்துகிறது.
எடுத்துக்காட்டாக, ஒரு வரிசையில் உள்ள ஒவ்வொரு உறுப்பையும் 10 காரணியால் அளவிட, நீங்கள் 10-கள் நிறைந்த ஒரு வரிசையை உருவாக்கத் தேவையில்லை. நீங்கள் வெறுமனே எழுதுகிறீர்கள்:
my_array = np.array([1, 2, 3, 4])
scaled_array = my_array * 10 # my_array முழுவதும் ஸ்கேலார் 10-ஐ பிராட்காஸ்டிங் செய்தல்
மேலும், தரவு நினைவகத்தில் அமைக்கப்பட்ட விதம் மிக முக்கியமானது. NumPy வரிசைகள் நினைவகத்தின் ஒரு தொடர்ச்சியான தொகுதியில் சேமிக்கப்படுகின்றன. இது SIMD-க்கு அவசியமானது, இது தரவை அதன் அகலமான பதிவேடுகளில் வரிசையாக ஏற்ற வேண்டும். நினைவக தளவமைப்பைப் புரிந்துகொள்வது (எ.கா., C-பாணி வரிசை-முதன்மை மற்றும் ஃபோர்டிரான்-பாணி நெடுவரிசை-முதன்மை) மேம்பட்ட செயல்திறன் சரிப்படுத்தலுக்கு முக்கியமானது, குறிப்பாக பல-பரிமாண தரவுகளுடன் வேலை செய்யும் போது.
எல்லைகளைத் தாண்டி: மேம்பட்ட SIMD நூலகங்கள்
பைத்தானில் வெக்டரைசேஷனுக்கான முதல் மற்றும் மிக முக்கியமான கருவி NumPy ஆகும். இருப்பினும், உங்கள் வழிமுறை நிலையான NumPy ufuncs-களைப் பயன்படுத்தி எளிதாக வெளிப்படுத்த முடியாதபோது என்ன நடக்கும்? ஒருவேளை உங்களிடம் சிக்கலான நிபந்தனை தர்க்கத்துடன் ஒரு லூப் இருக்கலாம் அல்லது எந்த நூலகத்திலும் கிடைக்காத ஒரு தனிப்பயன் வழிமுறை இருக்கலாம். இங்குதான் மேலும் மேம்பட்ட கருவிகள் வருகின்றன.
Numba: வேகத்திற்கான ஜஸ்ட்-இன்-டைம் (JIT) தொகுப்பு
Numba ஒரு குறிப்பிடத்தக்க நூலகமாகும், இது ஒரு ஜஸ்ட்-இன்-டைம் (JIT) கம்பைலராக செயல்படுகிறது. இது உங்கள் பைத்தான் குறியீட்டைப் படிக்கிறது, மற்றும் இயக்க நேரத்தில், அது பைத்தான் சூழலை விட்டு வெளியேறாமலேயே அதை மிகவும் உகந்ததாக்கப்பட்ட இயந்திர குறியீடாக மொழிபெயர்க்கிறது. இது குறிப்பாக லூப்களை மேம்படுத்துவதில் சிறந்தது, இது நிலையான பைத்தானின் முதன்மை பலவீனமாகும்.
Numba-வைப் பயன்படுத்த மிகவும் பொதுவான வழி அதன் டெக்கரேட்டர், `@jit` மூலம் ஆகும். NumPy-இல் வெக்டரைஸ் செய்ய கடினமான ஒரு எடுத்துக்காட்டை எடுத்துக் கொள்வோம்: ஒரு தனிப்பயன் உருவகப்படுத்துதல் லூப்.
import numpy as np
from numba import jit
# NumPy-இல் வெக்டரைஸ் செய்ய கடினமான ஒரு கற்பனையான செயல்பாடு
def simulate_particles_python(positions, velocities, steps):
for _ in range(steps):
for i in range(len(positions)):
# சில சிக்கலான, தரவு-சார்ந்த தர்க்கம்
if positions[i] > 0:
velocities[i] -= 9.8 * 0.01
else:
velocities[i] = -velocities[i] * 0.9 # நெகிழ்ச்சியற்ற மோதல்
positions[i] += velocities[i] * 0.01
return positions
# அதே செயல்பாடு, ஆனால் Numba JIT டெக்கரேட்டருடன்
@jit(nopython=True, fastmath=True)
def simulate_particles_numba(positions, velocities, steps):
for _ in range(steps):
for i in range(len(positions)):
if positions[i] > 0:
velocities[i] -= 9.8 * 0.01
else:
velocities[i] = -velocities[i] * 0.9
positions[i] += velocities[i] * 0.01
return positions
வெறுமனே `@jit(nopython=True)` டெக்கரேட்டரைச் சேர்ப்பதன் மூலம், இந்தச் செயல்பாட்டை இயந்திரக் குறியீடாகத் தொகுக்குமாறு Numba-விடம் கூறுகிறீர்கள். `nopython=True` வாதம் முக்கியமானது; இது Numba மெதுவான பைத்தான் மொழிபெயர்ப்பாளருக்குத் திரும்பாத குறியீட்டை உருவாக்குவதை உறுதி செய்கிறது. `fastmath=True` கொடி Numba-வை குறைவான துல்லியமான ஆனால் வேகமான கணித செயல்பாடுகளைப் பயன்படுத்த அனுமதிக்கிறது, இது தானாக-வெக்டரைசேஷனை இயக்க முடியும். Numba-வின் கம்பைலர் உள் லூப்பை பகுப்பாய்வு செய்யும் போது, நிபந்தனை தர்க்கத்துடன் கூட, ஒரே நேரத்தில் பல துகள்களைச் செயலாக்க SIMD அறிவுறுத்தல்களைத் தானாக உருவாக்க முடியும், இது கையால் எழுதப்பட்ட C குறியீட்டின் செயல்திறனைப் போட்டியிடும் அல்லது அதை விட அதிகமாக இருக்கும்.
Cython: பைத்தானை C/C++ உடன் கலத்தல்
Numba பிரபலமடைவதற்கு முன்பு, பைத்தான் குறியீட்டை வேகப்படுத்துவதற்கான முதன்மை கருவியாக Cython இருந்தது. Cython என்பது பைத்தான் மொழியின் ஒரு சூப்பர்செட் ஆகும், இது C/C++ செயல்பாடுகளை அழைப்பதையும் மற்றும் மாறிகள் மற்றும் வகுப்பு பண்புகளில் C வகைகளை அறிவிப்பதையும் ஆதரிக்கிறது. இது ஒரு அஹெட்-ஆஃப்-டைம் (AOT) கம்பைலராக செயல்படுகிறது. உங்கள் குறியீட்டை `.pyx` கோப்பில் எழுதுகிறீர்கள், அதை Cython ஒரு C/C++ மூலக் கோப்பாகத் தொகுக்கிறது, பின்னர் அது ஒரு நிலையான பைத்தான் நீட்டிப்பு தொகுதியாகத் தொகுக்கப்படுகிறது.
Cython-இன் முக்கிய நன்மை அது வழங்கும் நுண்ணிய-கட்டுப்பாடாகும். நிலையான வகை அறிவிப்புகளைச் சேர்ப்பதன் மூலம், பைத்தானின் டைனமிக் மேல்நிலையின் பெரும்பகுதியை நீங்கள் அகற்றலாம்.
ஒரு எளிய Cython செயல்பாடு இப்படி இருக்கலாம்:
# 'sum_module.pyx' என்ற கோப்பில்
def sum_typed(long[:] arr):
cdef long total = 0
cdef int i
for i in range(arr.shape[0]):
total += arr[i]
return total
இங்கு, `cdef` C-நிலை மாறிகளை (`total`, `i`) அறிவிக்கப் பயன்படுகிறது, மற்றும் `long[:]` உள்ளீட்டு வரிசையின் ஒரு தட்டச்சு செய்யப்பட்ட நினைவகப் பார்வையை வழங்குகிறது. இது Cython-ஐ மிகவும் திறமையான C லூப்பை உருவாக்க அனுமதிக்கிறது. நிபுணர்களுக்கு, Cython SIMD இன்ட்ரின்சிக்ஸை நேரடியாக அழைப்பதற்கான வழிமுறைகளையும் வழங்குகிறது, இது செயல்திறன்-முக்கியமான பயன்பாடுகளுக்கு இறுதி நிலை கட்டுப்பாட்டை வழங்குகிறது.
சிறப்பு நூலகங்கள்: சுற்றுச்சூழல் அமைப்பில் ஒரு பார்வை
உயர்-செயல்திறன் பைத்தான் சுற்றுச்சூழல் அமைப்பு பரந்தது. NumPy, Numba, மற்றும் Cython-க்கு அப்பால், பிற சிறப்பு கருவிகள் உள்ளன:
- NumExpr: நினைவகப் பயன்பாட்டை மேம்படுத்துவதன் மூலமும் மற்றும் `2*a + 3*b` போன்ற கோவைகளை மதிப்பிடுவதற்கு பல கோர்களைப் பயன்படுத்துவதன் மூலமும் சில நேரங்களில் NumPy-ஐ விட சிறப்பாக செயல்படக்கூடிய ஒரு வேகமான எண் கோவை மதிப்பீட்டாளர்.
- Pythran: பைத்தான் குறியீட்டின் ஒரு துணைக்குழுவை, குறிப்பாக NumPy-ஐப் பயன்படுத்தும் குறியீட்டை, மிகவும் உகந்ததாக்கப்பட்ட C++11-ஆக மொழிபெயர்க்கும் ஒரு அஹெட்-ஆஃப்-டைம் (AOT) கம்பைலர், இது பெரும்பாலும் தீவிரமான SIMD வெக்டரைசேஷனை செயல்படுத்துகிறது.
- Taichi: உயர்-செயல்திறன் இணை கணினிக்கு பைத்தானில் பதிக்கப்பட்ட ஒரு டொமைன்-குறிப்பிட்ட மொழி (DSL), குறிப்பாக கணினி வரைகலை மற்றும் இயற்பியல் உருவகப்படுத்துதல்களில் பிரபலமானது.
உலகளாவிய பார்வையாளர்களுக்கான நடைமுறை பரிசீலனைகள் மற்றும் சிறந்த நடைமுறைகள்
உயர்-செயல்திறன் குறியீட்டை எழுதுவது சரியான நூலகத்தைப் பயன்படுத்துவதை விட அதிகம். இங்கே சில உலகளவில் பொருந்தக்கூடிய சிறந்த நடைமுறைகள் உள்ளன.
SIMD ஆதரவை எவ்வாறு சரிபார்ப்பது
நீங்கள் பெறும் செயல்திறன் உங்கள் குறியீடு இயங்கும் வன்பொருளைப் பொறுத்தது. ஒரு குறிப்பிட்ட CPU ஆல் எந்த SIMD அறிவுறுத்தல் தொகுப்புகள் ஆதரிக்கப்படுகின்றன என்பதை அறிவது பெரும்பாலும் பயனுள்ளதாக இருக்கும். நீங்கள் `py-cpuinfo` போன்ற ஒரு கிராஸ்-பிளாட்பார்ம் நூலகத்தைப் பயன்படுத்தலாம்.
# இதனுடன் நிறுவவும்: pip install py-cpuinfo
import cpuinfo
info = cpuinfo.get_cpu_info()
supported_flags = info.get('flags', [])
print("SIMD ஆதரவு:")
if 'avx512f' in supported_flags:
print("- AVX-512 ஆதரிக்கப்படுகிறது")
elif 'avx2' in supported_flags:
print("- AVX2 ஆதரிக்கப்படுகிறது")
elif 'avx' in supported_flags:
print("- AVX ஆதரிக்கப்படுகிறது")
elif 'sse4_2' in supported_flags:
print("- SSE4.2 ஆதரிக்கப்படுகிறது")
else:
print("- அடிப்படை SSE ஆதரவு அல்லது பழையது.")
இது ஒரு உலகளாவிய சூழலில் மிக முக்கியமானது, ஏனெனில் கிளவுட் கம்ப்யூட்டிங் நிகழ்வுகள் மற்றும் பயனர் வன்பொருள் பிராந்தியங்கள் முழுவதும் பரவலாக வேறுபடலாம். வன்பொருள் திறன்களை அறிவது செயல்திறன் பண்புகளைப் புரிந்துகொள்ள அல்லது குறிப்பிட்ட தேர்வுமுறைகளுடன் குறியீட்டைத் தொகுக்க உதவும்.
தரவு வகைகளின் முக்கியத்துவம்
SIMD செயல்பாடுகள் தரவு வகைகளுக்கு (`dtype` NumPy-இல்) மிகவும் குறிப்பிட்டவை. உங்கள் SIMD பதிவேட்டின் அகலம் நிலையானது. இதன் பொருள் நீங்கள் ஒரு சிறிய தரவு வகையைப் பயன்படுத்தினால், நீங்கள் ஒரு ஒற்றை பதிவேட்டில் அதிக உறுப்புகளைப் பொருத்தலாம் மற்றும் ஒரு அறிவுறுத்தலுக்கு அதிக தரவைச் செயலாக்கலாம்.
எடுத்துக்காட்டாக, ஒரு 256-பிட் AVX பதிவேடு வைத்திருக்க முடியும்:
- நான்கு 64-பிட் மிதக்கும்-புள்ளி எண்கள் (`float64` அல்லது `double`).
- எட்டு 32-பிட் மிதக்கும்-புள்ளி எண்கள் (`float32` அல்லது `float`).
உங்கள் பயன்பாட்டின் துல்லியத் தேவைகளை 32-பிட் மிதவைகளால் பூர்த்தி செய்ய முடிந்தால், உங்கள் NumPy வரிசைகளின் `dtype`-ஐ `np.float64` (பல கணினிகளில் இயல்புநிலை) இலிருந்து `np.float32`-க்கு மாற்றுவது AVX-இயக்கப்பட்ட வன்பொருளில் உங்கள் கணக்கீட்டு செயல்திறனை இரட்டிப்பாக்க முடியும். உங்கள் பிரச்சனைக்கு போதுமான துல்லியத்தை வழங்கும் மிகச்சிறிய தரவு வகையை எப்போதும் தேர்வு செய்யவும்.
எப்போது வெக்டரைஸ் செய்யக்கூடாது
வெக்டரைசேஷன் ஒரு வெள்ளி தோட்டா அல்ல. அது பயனற்றதாகவோ அல்லது எதிர்விளைவாகவோ இருக்கும் சூழ்நிலைகள் உள்ளன:
- தரவு-சார்ந்த கட்டுப்பாட்டு ஓட்டம்: கணிக்க முடியாத மற்றும் மாறுபட்ட செயல்படுத்தல் பாதைகளுக்கு வழிவகுக்கும் சிக்கலான `if-elif-else` கிளைகளைக் கொண்ட லூப்களை கம்பைலர்கள் தானாக வெக்டரைஸ் செய்வது மிகவும் கடினம்.
- வரிசைமுறை சார்புகள்: ஒரு உறுப்பிற்கான கணக்கீடு முந்தைய உறுப்பின் முடிவைப் பொறுத்தது என்றால் (எ.கா., சில மறுசுழற்சி சூத்திரங்களில்), பிரச்சனை இயல்பாகவே வரிசையானது மற்றும் SIMD உடன் இணையாக இருக்க முடியாது.
- சிறிய தரவுத்தொகுப்புகள்: மிகச் சிறிய வரிசைகளுக்கு (எ.கா., ஒரு டஜன் உறுப்புகளுக்குக் குறைவானது), NumPy-இல் வெக்டரைஸ்டு செயல்பாட்டு அழைப்பை அமைப்பதற்கான மேல்நிலை ஒரு எளிய, நேரடி பைத்தான் லூப்பின் செலவை விட அதிகமாக இருக்கலாம்.
- ஒழுங்கற்ற நினைவக அணுகல்: உங்கள் வழிமுறை ஒரு கணிக்க முடியாத வடிவத்தில் நினைவகத்தில் சுற்றித் தாவ வேண்டும் என்றால், அது CPU-இன் கேச் மற்றும் முன்-பெறுதல் வழிமுறைகளைத் தோற்கடிக்கும், இது SIMD-இன் ஒரு முக்கிய நன்மையை ரத்து செய்யும்.
வழக்கு ஆய்வு: SIMD உடன் பட செயலாக்கம்
இந்தக் கருத்துக்களை ஒரு நடைமுறை உதாரணத்துடன் உறுதிப்படுத்துவோம்: ஒரு வண்ணப் படத்தை கிரேஸ்கேலுக்கு மாற்றுவது. ஒரு படம் என்பது எண்களின் 3D வரிசை (உயரம் x அகலம் x வண்ண சேனல்கள்), இது வெக்டரைசேஷனுக்கு ஒரு சரியான வேட்பாளராக அமைகிறது.
ஒளிர்வுக்கான ஒரு நிலையான சூத்திரம்: `கிரேஸ்கேல் = 0.299 * R + 0.587 * G + 0.114 * B`.
நம்மிடம் `(1920, 1080, 3)` வடிவத்திலும் `uint8` தரவு வகையிலும் ஒரு NumPy வரிசையாக ஏற்றப்பட்ட ஒரு படம் இருப்பதாகக் கொள்வோம்.
முறை 1: தூய பைத்தான் லூப் (மெதுவான வழி)
def to_grayscale_python(image):
h, w, _ = image.shape
grayscale_image = np.zeros((h, w), dtype=np.uint8)
for r in range(h):
for c in range(w):
pixel = image[r, c]
gray_value = 0.299 * pixel[0] + 0.587 * pixel[1] + 0.114 * pixel[2]
grayscale_image[r, c] = int(gray_value)
return grayscale_image
இதில் மூன்று உள்ளமைக்கப்பட்ட லூப்கள் உள்ளன, மேலும் இது ஒரு உயர்-தெளிவுத்திறன் படத்திற்கு நம்பமுடியாத அளவிற்கு மெதுவாக இருக்கும்.
முறை 2: NumPy வெக்டரைசேஷன் (வேகமான வழி)
def to_grayscale_numpy(image):
# R, G, B சேனல்களுக்கான எடைகளை வரையறுக்கவும்
weights = np.array([0.299, 0.587, 0.114])
# கடைசி அச்சில் (வண்ண சேனல்கள்) டாட் பெருக்கத்தைப் பயன்படுத்தவும்
grayscale_image = np.dot(image[...,:3], weights).astype(np.uint8)
return grayscale_image
இந்த பதிப்பில், நாங்கள் ஒரு டாட் பெருக்கத்தைச் செய்கிறோம். NumPy-இன் `np.dot` மிகவும் உகந்ததாக்கப்பட்டுள்ளது மற்றும் பல பிக்சல்களுக்கு ஒரே நேரத்தில் R, G, B மதிப்புகளைப் பெருக்கவும் மற்றும் கூட்டவும் SIMD-ஐப் பயன்படுத்தும். செயல்திறன் வேறுபாடு இரவுக்கும் பகலுக்கும் இடையில் இருக்கும் - எளிதாக ஒரு 100x வேகம் அல்லது அதற்கு மேல்.
எதிர்காலம்: SIMD மற்றும் பைத்தானின் வளரும் நிலப்பரப்பு
உயர்-செயல்திறன் பைத்தான் உலகம் தொடர்ந்து உருவாகி வருகிறது. பல த்ரெட்கள் பைத்தான் பைட் குறியீட்டை இணையாகச் செயல்படுத்துவதைத் தடுக்கும் famuos Global Interpreter Lock (GIL) சவால் செய்யப்படுகிறது. GIL-ஐ விருப்பமாக்குவதை நோக்கமாகக் கொண்ட திட்டங்கள் இணைத்தன்மைக்கு புதிய வழிகளைத் திறக்கக்கூடும். இருப்பினும், SIMD ஒரு துணை-கோர் மட்டத்தில் செயல்படுகிறது மற்றும் GIL-ஆல் பாதிக்கப்படவில்லை, இது ஒரு நம்பகமான மற்றும் எதிர்கால-ஆதார தேர்வுமுறை உத்தியாக அமைகிறது.
வன்பொருள் மேலும் பன்முகத்தன்மை வாய்ந்ததாக மாறும்போது, சிறப்பு முடுக்கிகள் மற்றும் மேலும் சக்திவாய்ந்த வெக்டர் அலகுகளுடன், வன்பொருள் விவரங்களைச் சுருக்கி அதே நேரத்தில் செயல்திறனை வழங்கும் கருவிகள்—NumPy மற்றும் Numba போன்றவை—இன்னும் முக்கியமானதாக மாறும். ஒரு CPU-க்குள் SIMD-இலிருந்து அடுத்த படி பெரும்பாலும் ஒரு GPU-வில் SIMT (ஒற்றை அறிவுறுத்தல், பல த்ரெட்கள்) ஆகும், மேலும் CuPy (NVIDIA GPU-களில் NumPy-க்கு ஒரு டிராப்-இன் மாற்று) போன்ற நூலகங்கள் இந்த அதே வெக்டரைசேஷன் கொள்கைகளை இன்னும் பெரிய அளவில் பயன்படுத்துகின்றன.
முடிவுரை: வெக்டரைத் தழுவுங்கள்
நாம் CPU-வின் மையத்திலிருந்து பைத்தானின் உயர்-நிலை சுருக்கங்களுக்கு பயணம் செய்துள்ளோம். பைத்தானில் வேகமான எண் குறியீட்டை எழுத, நீங்கள் லூப்களில் அல்ல, வரிசைகளில் சிந்திக்க வேண்டும் என்பதே முக்கிய எடுத்துக்கொள்ள வேண்டிய விஷயம். இதுவே வெக்டரைசேஷனின் சாராம்சம்.
நமது பயணத்தை சுருக்கமாகக் காண்போம்:
- பிரச்சனை: தூய பைத்தான் லூப்கள் மொழிபெயர்ப்பாளர் மேல்நிலை காரணமாக எண் பணிகளுக்கு மெதுவாக உள்ளன.
- வன்பொருள் தீர்வு: SIMD ஒரு CPU கோர் ஒரே நேரத்தில் பல தரவுப் புள்ளிகளில் ஒரே செயல்பாட்டைச் செய்ய அனுமதிக்கிறது.
- முதன்மை பைத்தான் கருவி: NumPy வெக்டரைசேஷனின் மூலக்கல்லாகும், இது ஒரு உள்ளுணர்வு வரிசைப் பொருளையும் மற்றும் உகந்ததாக்கப்பட்ட, SIMD-இயக்கப்பட்ட C/Fortran குறியீடாகச் செயல்படுத்தப்படும் ufuncs-இன் ஒரு வளமான நூலகத்தையும் வழங்குகிறது.
- மேம்பட்ட கருவிகள்: NumPy-இல் எளிதாக வெளிப்படுத்த முடியாத தனிப்பயன் வழிமுறைகளுக்கு, Numba உங்கள் லூப்களைத் தானாக மேம்படுத்த JIT தொகுப்பை வழங்குகிறது, அதே நேரத்தில் Cython பைத்தானை C உடன் கலப்பதன் மூலம் நுண்ணிய-கட்டுப்பாட்டை வழங்குகிறது.
- மனநிலை: பயனுள்ள தேர்வுமுறைக்கு தரவு வகைகள், நினைவக முறைகளைப் புரிந்துகொள்வது, மற்றும் வேலைக்கு சரியான கருவியைத் தேர்ந்தெடுப்பது தேவை.
அடுத்த முறை நீங்கள் ஒரு பெரிய எண்களின் பட்டியலைச் செயலாக்க `for` லூப்பை எழுதும்போது, நிறுத்தி கேளுங்கள்: "இதை ஒரு வெக்டர் செயல்பாடாக நான் வெளிப்படுத்த முடியுமா?" இந்த வெக்டரைஸ்டு மனநிலையைத் தழுவுவதன் மூலம், நீங்கள் நவீன வன்பொருளின் உண்மையான செயல்திறனைத் திறக்கலாம் மற்றும் உங்கள் பைத்தான் பயன்பாடுகளை வேகம் மற்றும் செயல்திறனின் ஒரு புதிய நிலைக்கு உயர்த்தலாம், நீங்கள் உலகில் எங்கிருந்து குறியீடு செய்தாலும் சரி.